import * as Cesium from "cesium"
import { defaultStyle } from "./style"
import * as CoordinatesTool from "../../utils/CoordinatesTool"
import { EventsConfig } from "../Draw/baseDrawers/DrawerBase"
import { BaseMeasure } from "./BaseMeasure"

/**
 * 面积测量工具类
 *
 */
export class AreaMeasure extends BaseMeasure {
  planePosition: Cesium.Cartesian3 | undefined //水平面定位点
  protected helpers: Cesium.Entity.ConstructorOptions[] = [
    //鼠标位置
    {
      position: new Cesium.CallbackPositionProperty(
        () => this.mousePosition,
        false,
      ),
      point: {
        ...this.styleOption?.point,
      },
    },
    //鼠标位置投影和虚线
    {
      properties: new Cesium.PropertyBag({
        type: "dash-line",
      }),
      position: new Cesium.CallbackPositionProperty(() => {
        if (this.mousePosition && this.planePosition) {
          return CoordinatesTool.projectPointToPlane(this.mousePosition, {
            point: this.planePosition,
            normal: this.getSurfaceNormal(this.planePosition),
          })
        } else {
          return undefined
        }
      }, false),
      point: {
        ...this.styleOption?.point,
      },
      polyline: {
        ...this.styleOption?.polyline,
        material: new Cesium.PolylineDashMaterialProperty({
          color: this.styleOption?.polyline?.material
            ? (this.styleOption.polyline.material as Cesium.Color)
            : defaultStyle.polyline?.material,
        }),
        positions: new Cesium.CallbackProperty(() => {
          if (this.mousePosition && this.planePosition) {
            return [
              CoordinatesTool.projectPointToPlane(this.mousePosition, {
                point: this.planePosition!,
                normal: this.getSurfaceNormal(this.planePosition!),
              }),
              this.mousePosition,
            ]
          } else {
            return undefined
          }
        }, false),
      },
    },
    //投影多边形
    {
      polygon: {
        ...this.styleOption?.polygon,
        hierarchy: new Cesium.CallbackProperty(() => {
          if (this.mousePosition) {
            return this.computeProjectedPolygonHierarchy([
              ...this.featurePositions.value,
              this.mousePosition,
            ])
          } else return undefined
        }, false),
      },
    },
    //多边形面积标注
    {
      position: new Cesium.CallbackPositionProperty(() => {
        if (this.mousePosition && this.featurePositions.value.length >= 2) {
          return CoordinatesTool.center(
            this.computeProjectedPolygonHierarchy([
              ...this.featurePositions.value,
              this.mousePosition,
            ]).positions,
          )
        } else return undefined
      }, false),
      label: {
        ...this.styleOption?.label,
        text: new Cesium.CallbackProperty(() => {
          //计算面积
          if (this.featurePositions.value.length >= 2 && this.mousePosition) {
            const area = CoordinatesTool.getPolygonArea(
              this.computeProjectedPolygonHierarchy([
                ...this.featurePositions.value,
                this.mousePosition,
              ]),
            ).toFixed(2)
            return area + "㎡"
          } else return ""
        }, false),
      },
    },
    //拾取点连线-实线
    {
      properties: new Cesium.PropertyBag({
        type: "bottom-line",
      }),
      polyline: {
        ...this.styleOption?.polyline,
        positions: new Cesium.CallbackProperty(() => {
          if (this.featurePositions.value.length >= 2) {
            return [
              ...this.featurePositions.value,
              this.mousePosition,
              this.featurePositions.value[0],
            ]
          } else {
            return [...this.featurePositions.value, this.mousePosition]
          }
        }, false),
      },
    },
  ]
  protected events: EventsConfig = {
    cesiumEvents: [
      // 鼠标移动
      {
        type: "mousemove",
        callback: (e: Cesium.ScreenSpaceEventHandler.MotionEvent) => {
          const position = this.viewer.scene.pickPosition(e.endPosition)
          if (position) {
            this.mousePosition = position

            //从拾取点和鼠标点中获取海拔最高点
            this.planePosition = this.computePlanePosition([
              ...this.featurePositions.value,
              this.mousePosition,
            ])
          }
        },
      },
      // 鼠标左键点击
      {
        type: "click",
        callback: (e: Cesium.ScreenSpaceEventHandler.PositionedEvent) => {
          const position = this.viewer.scene.pickPosition(e.position)
          if (position) {
            //更新顶点数组
            this.featurePositions.value.push(position)

            //绘制虚线和投影顶点
            this.drawDashLine(position)
          }
        },
      },
      // 鼠标右键点击
      {
        type: "rightclick",
        callback: (e: Cesium.ScreenSpaceEventHandler.PositionedEvent) => {
          const pickedPosition = this.scene.pickPosition(e.position)
          if (pickedPosition) {
            this.featurePositions.value.push(pickedPosition)
            //绘制
            this.drawMeasuredEntities()

            //初始化状态
            this.featurePositions.value = []
            this.removeHelpers()
            this.createHelpers()

            //结束
            this.end()
          }
        },
      },
    ],
  }

  //绘制虚线和投影到最高水平面的顶点
  drawDashLine(position: Cesium.Cartesian3) {
    if (this.planePosition) {
      const helper = this.entities.add({
        properties: new Cesium.PropertyBag({
          type: "dash-line",
        }),
        position: new Cesium.CallbackPositionProperty(() => {
          return CoordinatesTool.projectPointToPlane(position, {
            point: this.planePosition!,
            normal: this.getSurfaceNormal(this.planePosition!),
          })
        }, false),
        point: {
          ...this.styleOption?.point,
        },
        polyline: {
          ...this.styleOption?.polyline,
          material: new Cesium.PolylineDashMaterialProperty({
            color: Cesium.Color.ORANGE,
          }),
          positions: new Cesium.CallbackProperty(() => {
            return [
              CoordinatesTool.projectPointToPlane(position, {
                point: this.planePosition!,
                normal: this.getSurfaceNormal(this.planePosition!),
              }),
              position,
            ]
          }, false),
        },
      })
      this.helperEntities.push(helper)
    }
  }

  //从拾取点中计算出海拔最高点为水平定位点
  computePlanePosition(positions: Cesium.Cartesian3[]) {
    const hightestPosition = positions
      .map((item) => item.clone())
      .reduce((prev, curr) => {
        const prevCartographic = Cesium.Cartographic.fromCartesian(prev)
        const currCartographic = Cesium.Cartographic.fromCartesian(curr)
        return prevCartographic.height > currCartographic.height ? prev : curr
      })
    return hightestPosition
  }

  //获取地表法线
  private getSurfaceNormal(position: Cesium.Cartesian3) {
    return Cesium.Cartesian3.normalize(
      this.viewer.scene.globe.ellipsoid.geodeticSurfaceNormal(position),
      new Cesium.Cartesian3(),
    )
  }

  //计算最高投影面
  protected computeProjectedPolygonHierarchy(positions: Cesium.Cartesian3[]) {
    if (positions.length >= 3) {
      const planePosition = this.computePlanePosition(positions)
      //计算最高点法向量
      const normal = Cesium.Cartesian3.normalize(
        this.viewer.scene.globe.ellipsoid.geodeticSurfaceNormal(planePosition),
        new Cesium.Cartesian3(),
      )

      //计算投影多边形坐标
      const polygonPositions = positions.map((item) => {
        return CoordinatesTool.projectPointToPlane(item, {
          point: planePosition,
          normal,
        })
      })
      return new Cesium.PolygonHierarchy(polygonPositions)
    } else return new Cesium.PolygonHierarchy()
  }

  //绘制测定的实体
  protected drawMeasuredEntities() {
    const positions = this.featurePositions.value
    //绘制底部顶点、投影顶点、垂直虚线
    positions.forEach((item, index) => {
      //底部顶点
      this.drawFeature({
        position: new Cesium.CallbackPositionProperty(() => {
          return positions[index]
        }, false),
        point: {
          ...this.styleOption?.point,
        },
      })
      //投影顶点
      this.drawFeature({
        position: new Cesium.CallbackPositionProperty(
          () =>
            CoordinatesTool.projectPointToPlane(positions[index], {
              point: this.computePlanePosition(positions),
              normal: this.getSurfaceNormal(
                this.computePlanePosition(positions),
              ),
            }),
          false,
        ),
        point: {
          ...this.styleOption?.point,
        },
      })
      //垂直虚线
      this.drawFeature({
        polyline: {
          positions: new Cesium.CallbackProperty(
            () => [
              CoordinatesTool.projectPointToPlane(positions[index], {
                point: this.computePlanePosition(positions),
                normal: this.getSurfaceNormal(
                  this.computePlanePosition(positions),
                ),
              }),
              positions[index],
            ],
            false,
          ),
          ...this.styleOption?.polyline,
          material: new Cesium.PolylineDashMaterialProperty({
            color: this.styleOption?.polyline?.material
              ? (this.styleOption.polyline.material as Cesium.Color)
              : defaultStyle.polyline?.material,
          }),
        },
      })
    })

    //绘制动态底部线
    this.drawFeature({
      polyline: {
        positions: new Cesium.CallbackProperty(
          () => [...positions, positions[0]],
          false,
        ),
      },
    })

    //绘制动态面
    this.drawFeature({
      polygon: {
        hierarchy: new Cesium.CallbackProperty(
          () => this.computeProjectedPolygonHierarchy(positions),
          false,
        ),
        perPositionHeight: true,
      },
    })

    //绘制动态标注
    this.drawFeature({
      position: new Cesium.CallbackPositionProperty(
        () =>
          CoordinatesTool.projectPointToPlane(
            CoordinatesTool.center(positions),
            {
              point: this.computePlanePosition(positions),
              normal: this.getSurfaceNormal(
                this.computePlanePosition(positions),
              ),
            },
          ),
        false,
      ),
      label: {
        ...this.styleOption?.label,
        text: new Cesium.CallbackProperty(() => {
          const polygonHierarchy = new Cesium.PolygonHierarchy(positions)
          //计算面积
          return (
            CoordinatesTool.getPolygonArea(polygonHierarchy).toFixed(2) + "㎡"
          )
        }, false),
      },
    })

    //保存坐标引用——为了支持节点编辑
    this.measuredFeature.push({
      featureId: String(Date.now()),
      positions,
    })
  }

  //初始状态
  protected initStates(): void {
    this.featurePositions.value = []
    this.planePosition = undefined
  }
}
